package com.learn.spring_aop.annotation_config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 切面类
 *
 * @Component：交由Spring容器管理
 * @Aspect：AOP配置，配合@EnableAspectJAutoProxy注解使用才能被扫描到，@EnableAspectJAutoProxy通常添加在Spring应用的配置类上 <p>
 * <p>
 * 不同切入点表达式通过&&可使其在同一织入同时生效
 * <p>
 * <p>
 * expression表示切入点表达式，格式为 execution([访问修饰符] 返回值类型 包名.类名.方法名(参数))
 * · 访问修饰符可省略
 * · 返回值类型、包名、类名、方法名可用*表示任意
 * · 包名与类名间.表示该包下的类，..表示该包及其子包下的类
 * · 参数列表可用..表示任意
 * @annotation表示注解切入点表达式，格式为 @annotation(注解全路径)
 * <p>
 * <p>
 * 配置织入，即增强和切入点的动态组合过程
 * · before：目标方法执行前执行，无法获取返回值
 * · around：目标方法执行前后执行，若抛出异常则环绕后方法不执行，可以获取返回值
 * · after-returning：目标方法顺利完成后执行，若抛出异常则不执行，可以获取返回值
 * · after-throwing：目标方法抛出异常时执行，可以获取返回异常
 * · after：目标方法执行后执行，不管是否抛出异常都执行，无法获取返回值
 */
@Component
@Aspect
public class AspectCls {
    @Pointcut("execution(void com.learn.spring_aop.annotation_config.target.TargetImpl.*(..))")
    public void pointcut() {
    }
    
    @Before("AspectCls.pointcut()")
    public void before() {
        System.out.println("advice-before");
    }
    
    @Around("AspectCls.pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("advice-around-before");
        
        // 🌟🌟🌟执行目标方法并返回
        Object proceed = proceedingJoinPoint.proceed();
        
        System.out.println("advice-around-after");
        return proceed;
    }
    
    @AfterReturning(
            pointcut = "AspectCls.pointcut()",
            returning = "result"
    )
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("advice-after-returning");
    }
    
    @AfterThrowing(
            value = "AspectCls.pointcut()",
            throwing = "throwable"
    )
    public void afterThrowing(JoinPoint joinPoint, Throwable throwable) throws Throwable {
        joinPoint.getArgs();
        joinPoint.getTarget();
        joinPoint.getStaticPart();
        
        System.out.println("advice-after-throwing-" + throwable.getMessage());
    }
    
    @After("AspectCls.pointcut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("advice-after");
    }
}
